//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <core.h>
#include <_hal.h>
#include <_schedule.h>
#include <entry.h>
#include <mantle.h>
#include <init.h>

bool_t g_bPreemption = TRUE;

#if !(defined(_EVC) && defined(_mips))
EXTERN DECL_CODEINIT void SwitchToThread0()
{
#else
EXTERN __declspec( naked) void SwitchToThread0()
{
    ASM(
        "addiu  sp, sp, -0x14;"
        "sw     ra, 0x10(sp);");
#endif
    SWITCH_TO_THREAD0(GetThread0());
}

EXTERN_C void EnablePreemption()
{
    g_bPreemption = TRUE;

    if (InterlockedExchange(&GetCurrentThread()->m_bNeedReschedule, FALSE)) {
        Schedule();
    }
}

// When disable interrupt
INLINE Thread *SelectNextThread()
{
    // TODO:
    register Thread *pNextThread =
        g_softRealTimeScheduleClass.SelectNextThread();
    if (NULL != pNextThread) goto Exit;

    pNextThread = g_timeSharingScheduleClass.SelectNextThread();
    if (NULL != pNextThread) goto Exit;

    pNextThread = g_idleScheduleClass.SelectNextThread();
    assert(pNextThread);

Exit:
    return pNextThread;
}

EXTERN void Schedule()
{
#ifdef _DEBUG
    assert(PreemptionEnabled());
#else
    if (!PreemptionEnabled()) {
        return;
    }
#endif

    DzDisablePreemption();

    TIMEPEG_SCHEDULE_START

    register Thread *pCurrentThread = GetCurrentThread();
    assert(THREAD_END_MARKER == pCurrentThread->m_uEndMarker);

    uint32_t uFlags = SaveFlagsAndCli();
    if (pCurrentThread->IsRunning()) {
        pCurrentThread->m_pEffectiveScheduleClass->OnThreadSwitchOut(
                pCurrentThread);
    }
    RestoreIF(uFlags);

    Cli();

    register Thread *pNextThread = SelectNextThread();
    assert(pNextThread);
    assert(THREAD_END_MARKER == pNextThread->m_uEndMarker);

    // Switch thread
    if (pCurrentThread != pNextThread) {
        // Switch process context
        if (NULL != pNextThread->m_pOwner
            && pCurrentThread->m_pOwner != pNextThread->m_pOwner) {
            pNextThread->m_pOwner->Activate();
        }

        THREAD_CONTEXT_SWITCH(pCurrentThread, pNextThread);
    }

    Sti();

    TIMEPEG_SCHEDULE_STOP

    EnablePreemption();
    TryToSendReceiveDebugMsg(DEBUGGEE_PROCESS_STOP, 0);
}

EXTERN void CDECL ScheduleTimeout(void *pvParameter)
{
    assert(pvParameter);
    ((Thread *)pvParameter)->WakeUpOnTimeout();
}

EXTERN void ScheduleNoReturn()
{
    assert(PreemptionEnabled());

    DzDisablePreemption();

    TIMEPEG_SCHEDULE_START

    register Thread *pCurrentThread = GetCurrentThread();
    assert(pCurrentThread);
    assert(THREAD_END_MARKER == pCurrentThread->m_uEndMarker);

    Cli();

    register Thread *pNextThread = SelectNextThread();
    assert(pNextThread);
    assert(THREAD_END_MARKER == pNextThread->m_uEndMarker);
    assert(pCurrentThread != pNextThread);

    // Switch process context
    if (NULL != pNextThread->m_pOwner
        && pCurrentThread->m_pOwner != pNextThread->m_pOwner) {
        pNextThread->m_pOwner->Activate();
    }

    THREAD_CONTEXT_SWITCH_NO_RETURN(pNextThread);
    assert0();
}

EXTERN void NeedReschedule()
{
    GetCurrentThread()->m_bNeedReschedule = TRUE;
}

ScheduleClass   *g_pDefaultScheduleClass = &g_timeSharingScheduleClass;
uint_t          g_uDefaultSchedulePolicy = 0;

EXTERN void GetScheduleClass(
        ThreadPriorityRank priorityRank,
        ScheduleClass **ppScheduleClass, uint_t *puSchedulePolicy)
{
    assert(ppScheduleClass);
    assert(puSchedulePolicy);

    switch (priorityRank) {
    case ThreadPriorityRank_Idle:
        *ppScheduleClass    = &g_idleScheduleClass;
        *puSchedulePolicy   = 0;
        break;
    case ThreadPriorityRank_Normal:
        *ppScheduleClass    = &g_timeSharingScheduleClass;
        *puSchedulePolicy   = 0;
        break;
    case ThreadPriorityRank_SoftRealTime:
        *ppScheduleClass    = &g_softRealTimeScheduleClass;
        *puSchedulePolicy   = SoftRealTime_FIFO;
        break;
    case ThreadPriorityRank_HardRealTime:
        // TODO:
    default:
        assert0();
    }
}

EXTERN DECL_CODEINIT bool_t InitSchedule()
{
    g_idleScheduleClass.Initialize();
    g_timeSharingScheduleClass.Initialize();
    g_softRealTimeScheduleClass.Initialize();

    return TRUE;
}
